package mule.model;

//<editor-fold defaultstate="collapsed" desc="Imports">

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import javafx.beans.property.IntegerProperty;
import mule.model.enums.Race;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.paint.Color;

//</editor-fold>

/**
 * Player represents a human player that is playing the game. They have properties
 * which they select in the PlayerOptions screen and an inventory, which is used
 * to calculate their score.
 *
 * @author stephen
 */
public class Player implements Serializable, Comparable 
{
    // <editor-fold defaultstate="collapsed" desc="Properties">

    private int score;
    private StringProperty name;
    private ObjectProperty<Race> race;
    private ObjectProperty<Color> color;
    private Mule mule;
    private IntegerProperty money;
    private IntegerProperty food;
    private IntegerProperty energy;
    private IntegerProperty ore;
    public ArrayList<Tile> ownedTiles;
    private IntegerProperty numberOfOwnedTiles;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Constructor">

    /**
     * Constructs a player. Give them a passed in name, race, and color. Also
     * calculates the money to give them to start with based on their race. The
     * players are all given 8 food, 4 energy, and 0 ore to start out with.
     * 
     * @param name The name of the player
     * @param race The race of the player
     * @param color The color of the player
     */
    public Player( String name, Race race, Color color )
    {
        ownedTiles = new ArrayList();
        this.name = new SimpleStringProperty( name );
        this.race = new SimpleObjectProperty( race );
        this.color = new SimpleObjectProperty( color );
        if( race.equals( Race.HUMANOID ) )
        {
            this.money = new SimpleIntegerProperty( 600 );
        }
        else if( race.equals( Race.FLAPPER ) )
        {
            this.money = new SimpleIntegerProperty( 1600 );
        }
        else
        {
            this.money = new SimpleIntegerProperty( 1000 );
        }
        this.food = new SimpleIntegerProperty( 8 );
        this.energy = new SimpleIntegerProperty( 4 );
        this.ore = new SimpleIntegerProperty( 0 );
        this.mule = null;
        score = 0;
    }
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Getters and Setters">
    
    /**
     * Returns the score of the player as an integer
     * 
     * @return score as an integer
     */
    public int getScore()
    {
        return score;
    }
    
    /**
     * Returns a StringProperty representation of the name field
     * 
     * @return name as a StringProperty 
     */
    public StringProperty nameProperty()
    {
        if( name == null )
        {
            name = new SimpleStringProperty( "Player", "Default" );
        }

        return name;
    }

    /**
     * Returns the name of the player as a String
     * 
     * @return String representation of the player's name 
     */
    public String getName()
    {
        return nameProperty().get();
    }
    
    public void updateScore()
    {
        score = money.get() + 10*food.get() + 20*energy.get() + 30*ore.get();
    }
    /**
     * Sets the name of a player to a String
     * 
     * @param name The String representation of the name to set for the player
     */
    public void setName( String name )
    {
        nameProperty().set( name );
    }

    /**
     * Returns the player's race as an ObjectProperty
     * 
     * @return The player's race
     */
    public ObjectProperty<Race> raceProperty()
    {
        if( race == null )
        {
            race = new SimpleObjectProperty( Race.values()[1], "Default" );
        }

        return race;
    }

    /**
     * Returns the Race of a player as a String representation
     * 
     * @return the Race of a player as a String
     */
    public String getRace()
    {
        return raceProperty().get().toString();
    }

    /**
     * Sets the player race to the Race interpretation of a given String
     * 
     * @param race String representation of a Race enumeration type
     */
    public void setRaceWithString( String race )
    {
        raceProperty().set( Race.valueOf( race ) );
    }

    /**
     * Alternate setter for race using the actual enumeration value
     * 
     * @param race The race to set 
     */
    public void setRace( Race race )
    {
        raceProperty().set( race );
    }

    /**
     * Returns an ObjectProperty representation of the player color
     * 
     * @return ObjectProperty representation of the player color
     */
    public ObjectProperty<Color> colorProperty()
    {
        if( color == null )
        {
            color = new SimpleObjectProperty( Color.WHITE, "Default" );
        }

        return color;
    }

    /**
     * Gets the Color representation of player color
     * 
     * @return The player's color
     */
    public Color getColor()
    {
        return colorProperty().get();
    }

    /**
     * Sets the player color
     * 
     * @param color Color to set on this player's profile
     */
    public void setColor( Color color )
    {
        colorProperty().set( color );
    }

    /**
     * Returns an IntegerProperty representation of the player's money
     * 
     * @return The IntegerProperty of money that a player has
     */
    public IntegerProperty moneyProperty()
    {
        if( money == null )
        {
            money = new SimpleIntegerProperty( 0, "Default" );
        }

        return money;
    }

    /**
     * Returns the player money as an integer
     * 
     * @return money the player currently has
     */
    public int getMoney()
    {
        return moneyProperty().get();
    }

    /**
     * Sets the player's money
     * 
     * @param money amount of money to set
     */
    public void setMoney( int money )
    {
        moneyProperty().set( money );
    }

    /**
     * Returns the food property of this player as an IntegerProperty
     * 
     * @return the foodProperty for a player
     */
    public IntegerProperty foodProperty()
    {
        if( food == null )
        {
            food = new SimpleIntegerProperty( 0, "Default" );
        }

        return food;
    }

    /**
     * Returns the food a player has as an integer
     * 
     * @return the amount of food a player has
     */
    public int getFood()
    {
        return foodProperty().get();
    }

    /**
     * Sets the food in a player's stockpile to a new value
     * 
     * @param food the amount of food to set for the player
     */
    public void setFood( int food )
    {
        foodProperty().set( food );
    }

    /**
     * Returns an IntegerProperty representation of the player energy
     * 
     * @return the energy of the player as an IntegerProperty
     */
    public IntegerProperty energyProperty()
    {
        if( energy == null )
        {
            energy = new SimpleIntegerProperty( 0, "Default" );
        }

        return energy;
    }

    /**
     * Returns an integer representation of the player energy
     * 
     * @return the energy of the player
     */
    public int getEnergy()
    {
        return energyProperty().get();
    }

    /**
     * Sets the energy of the player to a different value
     * 
     * @param energy the energy to set for the player
     */
    public void setEnergy( int energy )
    {
        energyProperty().set( energy );
    }

    /**
     * Returns an IntegerProperty representation of the player's ore
     * 
     * @return The player ore as an integer property
     */
    public IntegerProperty oreProperty()
    {
        if( ore == null )
        {
            ore = new SimpleIntegerProperty( 0, "Default" );
        }

        return ore;
    }

    /**
     * Returns the ore that a player has as an integer
     * 
     * @return the amount of ore that the player has
     */
    public int getOre()
    {
        return oreProperty().get();
    }

    /**
     * Sets the ore to a value that is passed in for a player
     * 
     * @param ore The new amount of ore to set for the player
     */
    public void setOre( int ore )
    {
        oreProperty().set( ore );
    }


    /**
     * Adds a tile to the player.
     * 
     * @param tile tile to add to the player
     */
    public void addTileToPlayer( Tile tile )
    {
        ownedTiles.add( tile );
    }

    /**
     * Returns the number of tile that a player owns
     * 
     * @return the number of tiles that a player owns
     */
    public int getNumberOfOwnedTiles()
    {
        return ownedTiles.size();
    }

    /**
     * Returns the mule object that a player could have
     * 
     * @return The mule attached to the player 
     */
    public Mule getMule()
    {
        return mule;
    }

    /**
     * Constructs a new mule and then attaches it to the player in order
     * to improve tiles with and outfit it.
     */
    public void addMule()
    {
        mule = new Mule();
    }

    /**
     * Deletes the mule from the player.
     */
    public void removeMule()
    {
        mule = null;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Methods">

    /**
     * Loops through the Player's tiles and calculates their production.
     */
    public void produce(){
        int energyProduced = 0, foodProduced = 0, oreProduced = 0, energyLost = 0;
        for(Tile t: ownedTiles){
            if(t.hasMule() && energy.greaterThan( 0 ).get()){
                energy.set( getEnergy() - 1 );
                energyLost++;
                energy.set( getEnergy() + t.getEnergyProduction() );
                energyProduced += t.getEnergyProduction();
                food.set( getFood() + t.getFoodProduction() );
                foodProduced += t.getFoodProduction();
                ore.set( getOre() + t.getOreProduction() );
                oreProduced += t.getOreProduction();
            }               
        }
        GameModel model = GameModel.getInstance();        
        model.setInformationText( model.getInformationText().concat("\n\nEnergy Produced: " + energyProduced + "\nEnergy Lost: " 
                + energyLost + "\nFood Produced: " + foodProduced + 
                "\nOre Produced: " + oreProduced) );        
    }
    
    @Override
    public String toString()
    {
        return "Player: " + getName(); 
    }

    /**
     * Custom toString-like method to return the color of a player as a string.
     * 
     * @return A readable version of the player's color
     */
    public String colorString()
    {
        if( color.get() == Color.RED )
        {
            return "red";
        }
        else if( color.get() == Color.WHITE )
        {
            return "white";
        }
        else if( color.get() == Color.BLUE )
        {
            return "blue";
        }
        else
        {
            return "green";
        }
    }
    // </editor-fold>

    @Override
    public int compareTo(Object o) {
        return score - ((Player)o).getScore();
    }
}
